<?php

namespace App\Task;


use App\Model\Applet;
use App\Model\DataBackhaulSetting;
use App\Model\DataPaymentBackhaul;
use App\Model\DataSpread;
use App\Model\PlayletBind;
use App\Model\Payment;
use App\Model\SystemOrderDay;
use App\Model\SystemOrderDayCount;
use GuzzleHttp\Client;
use Hyperf\Crontab\Annotation\Crontab;
use Hyperf\DbConnection\Db;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Utils\ApplicationContext;
use Psr\Log\LoggerInterface;

class PaymentSettleTask
{
    protected LoggerInterface $logger;

    public static $getTokenUrl = "https://open.douyin.com/oauth/client_token/";

    public static $createSettleUrl = "https://open.douyin.com/api/trade_basic/v1/developer/settle_create";
    public function __construct(protected LoggerFactory $loggerFactory)
    {
        $this->logger = $this->loggerFactory->get('log', 'PaymentSettle');
    }

    public function execute()
    {


        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Hyperf\Redis\RedisFactory::class)->get('default');
        $this->logger->info(date('Y-m-d H:i:s') . '------------------------获取7天前的内的订单');

        $time = time()-86400*7;
        $startTime = time()-86400*7-10800;

        $startDate = date('Y-m-d H:i:s',$startTime);
        $endDate = date('Y-m-d H:i:s',$time);

        $this->logger->info('开始时间为：'.$startDate);
        $this->logger->info('结束时间为：'.$endDate);
        try {
            $orderKey =  'payment_settle_live';
            $keyData = $redis->get($orderKey);
            if(!empty($keyData)){
//                $redis->del($orderKey);
                $this->logger->info('存在处理中的订单进程');
                $this->logger->info(date('Y-m-d H:i:s') . '------------------------结束定时获取订单进行分账');
                return;
            }else{
                $redis->set($orderKey,1);
            }
            //当前时间7天前，2个小时内的订单，半个小时跑一次脚本
            $paymentData = Payment::query()
                ->select(['data_payment.id','data_payment.type','data_payment.status','data_payment.applet_id','data_payment.pay_time','data_payment.status','data_payment.order_id'])
                ->where('type','=',2)
                ->where('pay_time','>=',$startDate)
                ->where('pay_time','<=',$endDate)
                ->where('data_payment.status','=',2)
                ->leftjoin('data_payment_settle', 'data_payment.id', '=', 'data_payment_settle.payment_id')
                ->whereNull('data_payment_settle.id')
                ->orderBy('pay_time','asc')
                ->get();

            //获取所有抖音小程序
            $appletData = Applet::query()->where(['type' => 2])->select(['id','app_id','app_secret'])->get();

            if($paymentData->isNotEmpty() && $appletData->isNotEmpty()) {

                $this->logger->info('订单的数据为数量为：', [count($paymentData)]);

                //整合小程序信息
                $appletKeyVal = array();

                foreach ($appletData as $appletDatum){

                    $appletKeyVal[$appletDatum->id] = $appletDatum->toArray();
                }
                $insertData = array();
                foreach ($paymentData as $paymentDatum) {

                    $applet_id = $paymentDatum->applet_id;

                    $out_order_no = $paymentDatum->order_id;
                    $this->logger->info('小程序ID：', [$applet_id]);
                    $this->logger->info('订单ID：', [$paymentDatum->id]);
                    //已支付
                    if ($paymentDatum->status == 2) {
                        //查询是否有对应的小程序信息
                        if(!empty($appletKeyVal[$applet_id])){

                            $appletInfo = $appletKeyVal[$applet_id];

                            //本地测试
                            if(!empty(env('DY_TEST_TOKEN'))){
                                $accessToken = env('DY_TEST_TOKEN');
                            }else{
                                $accessToken = $this->getDyAccessToken($appletInfo['id'],$appletInfo['app_id'],$appletInfo['app_secret']);
                            }
                            if(!empty($accessToken)){
                                //发起分账
                                $out_settle_no = 'S'.$out_order_no;

                                $insertInfo = [
                                    'payment_id' => $paymentDatum->id,
                                    'applet_id'  => $applet_id,
                                    'out_settle_no'  => 'S'.$out_order_no,
                                    'out_order_no'  => $out_order_no,
                                    'status'    => 1,
                                    'created_at' => date('Y-m-d H:i:s',time()),
                                    'updated_at' => date('Y-m-d H:i:s',time())
                                ];

                                $settleInfo = $this->settleCreate($out_order_no,$out_settle_no,$appletInfo['app_id'],$accessToken);
                                if(!empty($settleInfo)){
                                    $insertInfo['settle_id'] = $settleInfo['settle_id'];
                                    $insertInfo['wallet_settle_id']  = $settleInfo['wallet_settle_id'];
                                    $insertInfo['status'] = 2;
                                    $insertData[] = $insertInfo;
                                }else{
                                    $this->logger->error('发起分账错误',[$paymentDatum->id]);
                                }

                            }else{
                                $this->logger->error('token出现错误',[$paymentDatum->id]);
                            }
                        }else{
                            $this->logger->error('订单没有对应的小程序',[$paymentDatum->id]);
                        }
                    }
                }
                if(!empty($insertData)){
                    $this->logger->info('插入数据',[count($insertData)]);

                    $commonData = [];
                    $count = count($insertData);
                    foreach ($insertData as $key  => $val){
                        $commonData[] = $val;

                        if(count($commonData) > 2000){
                            Db::table('data_payment_settle')->insert($commonData);
                            $commonData = array();
                        }
                        if($key+1 == $count){
                            Db::table('data_payment_settle')->insert($commonData);
                            $commonData = array();
                        }
                    }
                }
            } else{
                $this->logger->info('没有订单需要处理');
            }
            $redis->del($orderKey);

        }catch (\Exception $e){
            $redis->del($orderKey);
            $this->logger->error('请求订单|错误：' . $e->getLine() . '|' . $e->getMessage());
        }
        $this->logger->info(date('Y-m-d H:i:s') . '------------------------结束定时获取订单数据');
    }

    public function getDyAccessToken($applet_id,$app_id,$app_secret){

        $container = ApplicationContext::getContainer();

        $redis = $container->get(\Hyperf\Redis\RedisFactory::class)->get('dy');

        $key = 'dy_admin_access_token';
        $key .= $applet_id;
        $accessToken = $redis->get($key);
        if(empty($accessToken)){
            $params         = [
                'grant_type' => 'client_credential',
                'client_key'    => $app_id,
                'client_secret'   => $app_secret
            ];
            //获取token
            $accessTokenInfo     = $this->request($params, self::$getTokenUrl,'POST');
            if (!empty($accessTokenInfo['data']['access_token'])){
                $accessToken = $accessTokenInfo['data']['access_token'];
                $redis->setex($key,7100,$accessToken);
            }else{
                $accessToken = "";
            }
        }
        $this->logger->info('抖音token为：'.$accessToken);
        return $accessToken;
    }

    public function settleCreate($out_order_no,$out_settle_no,$app_id,$accessToken,$num=0){

        try {
            if($num < 5 ){
                $num = $num+1;
            }else{
                return false;
            }
            $params         = [
                "app_id"   =>$app_id,
                "out_order_no"   => $out_order_no,
                "out_settle_no"  => $out_settle_no,
                "settle_desc"    => '系统自动分账'
            ];
            $taskUrl = self::$createSettleUrl;
            $this->logger->info('第'.$num.'次尝试请求',[$num]);
            $this->logger->info('发起分账信息数据：'.PHP_EOL.var_export(['result' => $params], true));
            $this->logger->info('发起分账信息Url：'.$taskUrl);
            $postResult  = $this->request($params, $taskUrl,'POST',$accessToken);
            if($postResult['err_no'] == 0){
                $this->logger->info('发起分账信息接口成功 返回数据：'.PHP_EOL.var_export(['result' => $postResult], true));
                return $postResult['data'];
            }else{
                $this->logger->error('发起分账信息接口失败 返回数据：'.PHP_EOL.var_export(['result' => $postResult], true));

                if($postResult['err_no'] == '23111'){
                    return ['settle_id' => 0,'wallet_settle_id' => 0];
                }


                usleep(30000);
                return $this->settleCreate($out_order_no,$out_settle_no,$app_id,$accessToken,$num);
            }
        }catch (\Exception $e){
            $this->logger->error('请求抖音分账错误'.$e->getMessage(),[$e->getLine()]);
        }
        return false;
    }


    /**
     * 传入数组进行HTTP POST请求
     */
    function request(array $params, string $url,string $method,string $access_token = null)
    {
        $client   = new Client([
            'timeout' => 30
        ]);
        $headers       = ['Content-Type' => 'application/json'];
        if(!empty($access_token)){
            $headers['access-token'] = $access_token;
        }

        $requestParams = [
            'headers' => $headers,
        ];
        if ($method == 'GET') {
            $requestParams['query'] = $params;
        } else {
            $requestParams['json'] = $params;//json_encode($params,JSON_UNESCAPED_UNICODE);
        }
        $response = $client->request($method,$url, $requestParams);
        if ($response->getStatusCode() !== 200) {
            return '';
        }
        $result   = $response->getBody()->getContents();
        $resultArr = json_decode($result,true);
        return $resultArr;

    }
}